#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;

#define CV(addr) ((struct sockaddr *)(&(addr)))

enum
{
    Sock_Err = 1,
    Bind_Err,
    Listen_Err,

};

class Socket
{
protected:
    virtual void CreateSockFd() = 0;
    virtual void BindSockFd(int port) = 0;
    virtual void ListenSockFd(int backlog) = 0;
    virtual bool ConnectSockFd(const string &serverip, uint16_t serverport) = 0;
    virtual int AcceptSockFd(string *peerip, uint16_t *peerport) = 0;

public:
    virtual ~Socket()
    {
    }

    virtual int GetSockFd() = 0;
    virtual void SetSockFd(int sockfd) = 0;
    virtual void CloseSockFd() = 0;
    virtual bool Recv(string &message, size_t size) = 0;
    virtual void Send(string message) = 0;

public:
    void BuildListenSock(int port, int backlog)
    {
        CreateSockFd();
        BindSockFd(port);
        ListenSockFd(backlog);
    }

    bool BuildClientFSock(const string &serverip, uint16_t serverport)
    {
        CreateSockFd();
        return ConnectSockFd(serverip, serverport);
    }

    int BuildServerSock(string *peerip, uint16_t *peerport)
    {
        return AcceptSockFd(peerip, peerport);
    }
};

const int defaultsockfd = -1;

class TcpSocket : public Socket
{
public:
    TcpSocket(int sockfd = defaultsockfd) : _sockfd(sockfd)
    {
    }

    int GetSockFd() override
    {
        return _sockfd;
    }
    virtual void SetSockFd(int sockfd) override
    {
        _sockfd = sockfd;
    }

    virtual void CloseSockFd() override
    {
        close(_sockfd);
    }

    bool Recv(string &message, size_t size) override
    {
        char buffer[size];
        ssize_t n = recv(_sockfd, buffer, size - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            message += buffer;
            return true;
        }
        else if (n == 0)
        {
            return false;
        }
        else
        {
            return false;
        }
    }
    void Send(string message) override
    {
        ssize_t n = send(_sockfd, message.c_str(), message.size(), 0);
        if(n < 0)
        {
            exit(1);
        }
    }

    virtual ~TcpSocket()
    {
    }

private:
    void CreateSockFd() override
    {
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
            exit(Sock_Err);

        int opt = 1;
        setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    }

    void BindSockFd(int port) override
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_addr.s_addr = INADDR_ANY;
        local.sin_port = htons(port);

        int n = bind(_sockfd, CV(local), sizeof(local));
        if (n < 0)
            exit(Bind_Err);
    }
    void ListenSockFd(int backlog) override
    {
        int n = listen(_sockfd, backlog);
        if (n < 0)
            exit(Listen_Err);
    }

    bool ConnectSockFd(const string &serverip, uint16_t serverport) override
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = inet_addr(serverip.c_str());
        server.sin_port = htons(serverport);

        int n = connect(_sockfd, CV(server), sizeof(server));
        if (n < 0)
            return false;
        else
            return true;
    }
    int AcceptSockFd(string *peerip, uint16_t *peerport) override
    {
        struct sockaddr_in peer;
        socklen_t len = sizeof(peer);
        int sockfd = accept(_sockfd, CV(peer), &len);
        if (sockfd > defaultsockfd)
        {
            *peerip = inet_ntoa(peer.sin_addr);
            *peerport = ntohs(peer.sin_port);
            // Socket *newsock = new TcpSocket(sockfd);
            return sockfd;
        }
        else
            return -1;
    }

private:
    int _sockfd;
};
